/*
 *  Copyright 2008-2010 www.jrails.org
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package org.rails.core.web;

import java.io.IOException;
import java.io.Writer;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.rails.core.GLOBAL;
import org.rails.core.model.ActiveRecord;
import org.rails.core.util.Utils;

/**
 * 请求动作管理类,负责控制每个客户端请求的指向和跳转,各控制器下有一个默认的Action对象。
 * @author jack
 *
 */
public final class Action {
	
	private HttpServletRequest request;
	private HttpServletResponse response;
	private ServletConfig config;
	private String path;	
	private String contextPath;
	private String controllerName;
	private String action;
	private String actionName;
	private String suffix;
	
	public static String CONTROLLER_PACK = "app.controller";
	
	private boolean active = true;

	public Action(ServletConfig config, HttpServletRequest request,
			HttpServletResponse response) throws PathException {
		path = request.getServletPath();
		contextPath = config.getServletContext().getContextPath();
		this.config = config;
		this.request = request;
		this.response = response;
		init();
	}
	
	private void init() throws PathException{
		final String[] p = path.split("/");
		if(p.length != 3)
			throw new PathException();
		else{
			controllerName = p[1];
			suffix = parseSuffix();
			action = p[2].replace(suffix,"");
			actionName = Utils.format("{0}Action", action);
		}		
	}

	private String parseSuffix() {
		final Pattern p = Pattern.compile("[\\.]{1}\\w{1,}$");
		final Matcher m = p.matcher(path);
		if (m.find())
			return m.group();
		else
			return "";
	}

	private void render(String controller, String action)
			throws IOException, ServletException {
		this.active = false;
		final String view = Utils.format("/view/{0}/{1}.jsp",
				controller, action);
		request.getRequestDispatcher(view).forward(request, response);
	}

	/**
	 * 渲染到约定的页面。
	 * @throws IOException
	 * @throws ServletException
	 */
	public void render() throws IOException, ServletException {
		render(controllerName, action);
	}

	/**
	 * 请求转发到指定的控制器和动作上。
	 * @param controller 目标控制器。
	 * @param action 目标动作。
	 * @throws IOException
	 * @throws ServletException
	 */
	public void forward(String controller, String action)
			throws IOException, ServletException {
		this.active = false;
		request.getRequestDispatcher(
				Utils.format("/{0}/{1}", controller, action))
				.forward(request, response);
	}

	/**
	 * 请求转发到当前控制器的动作上。
	 * @param action 目标动作。
	 * @throws IOException
	 * @throws ServletException
	 */
	public void forward(String action) throws IOException, ServletException {
		forward(controllerName, action);
	}
	
	/**
	 * 请求重定向到指定的控制器和动作上。
	 * @param controller 目标控制器。
	 * @param action 目标动作。
	 * @param queryString 查询字符串。
	 * @throws IOException
	 */
	public void redirect(String controller, String action,String queryString) throws IOException {
		this.active = false;
		queryString = Utils.isEmpty(queryString) ? "" : "?" + queryString;
		response.sendRedirect(Utils.format("../{0}/{1}{2}",controller, action,queryString));
	}

	/**
	 * 请求重定向到当前控制器的动作上。
	 * @param action 目标动作。
	 * @param queryString 查询字符串。
	 * @throws IOException
	 */
	public void redirect(String action,String queryString) throws IOException {
		redirect(controllerName, action,queryString);
	}
	
	/**
	 * 请求重定向到当前控制器的动作上。
	 * @param action 目标动作。
	 * @throws IOException
	 */
	public void redirect(String action) throws IOException {
		redirect(controllerName, action,null);
	}
	
	/**
	 * 响应流。
	 * @return 返回 java.io.Writer。
	 * @throws IOException
	 */
	public Writer out() throws IOException {
		response.setContentType(Utils.format("text/html; charset={0}",GLOBAL.RESOURCE_CHARSET_NAME));
		return response.getWriter();
	}
	
	public Object get(String key){
		return request.getAttribute(key);
	}
	
	public void set(String key,Object value){
		request.setAttribute(key,value);
	}
	
	public void bind(ActiveRecord record){
		List<String> atts = null;
		atts = record.getAttributes();
		String k;
		final Map<String,Object> m = new HashMap<String,Object>();
		for (Iterator<String> iterator = atts.iterator(); iterator
				.hasNext();) {
			k = iterator.next();
			m.put(k,record.get(k));					
		}
		set(record.getClass().getSimpleName(),m);
	}
	
	public void bind(String name,ActiveRecord record){
		List<String> atts = null;
		atts = record.getAttributes();
		String k;
		final Map<String,Object> m = new HashMap<String,Object>();
		for (Iterator<String> iterator = atts.iterator(); iterator
				.hasNext();) {
			k = iterator.next();
			m.put(k,record.get(k));					
		}
		set(name,m);
	}
	
	@SuppressWarnings("unchecked")
	public void bind(Map<String,Object> params){
		final List<String> keys = Utils.getKeys(params);
		final Map<String,Object> map = new HashMap<String,Object>();
		for (Iterator<String> iterator = keys.iterator(); iterator.hasNext();) {
			final String key = iterator.next();
			final Pattern pat = Pattern.compile("^[\\w]+[\\.]{0,1}");
			final Matcher mat = pat.matcher(key);
			if(mat.find()){				
				final String group = mat.group();
				final String k = group.replace(".","");
				if(group.indexOf(".") > -1){
					Map<String,Object> mm = new HashMap<String,Object>();
					if(map.containsKey(k))
						 mm = (Map<String, Object>) map.get(k);
									
					mm.put(mat.replaceFirst(""),params.get(key));
					map.put(k,mm);
				}else{
					map.put(k,params.get(key));
				}				
			}			
		}		
		set("params",map);
	}

	public void setActive(boolean active){
		this.active = active;
	}
	
	public boolean isActive() {
		return active;
	}

	public String getContextPath() {
		return contextPath;
	}

	@SuppressWarnings("unchecked")
	public Class<? extends ControllerBase> getController() throws ClassNotFoundException {
		return (Class<ControllerBase>) Class.forName(Utils.format("{0}.{1}Controller",CONTROLLER_PACK, controllerName));
	}

	public String getActionName() {
		return actionName;
	}
	
	public String getControllerName(){
		return controllerName;
	}
	
	public String getAction(){
		return action;
	}

	public HttpServletRequest getRequest() {
		return request;
	}

	public HttpServletResponse getResponse() {
		return response;
	}

	public ServletConfig getConfig() {
		return config;
	}
	
	public String getSuffix(){
		return suffix;
	}

}
